/*
 * Copyright 2012 Alain Pannetrat (L1L1@gmx.com)
 * 
 * This file is part of cookie-miner.
 * 
 * cookie-miner is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version.
 * 
 * cookie-miner is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License 
 * along with cookie-miner. If not, see http://www.gnu.org/licenses/.
 *
 * @version: $Id$
 *
 */
#include "json.h"
#include <cstdio>
#include "log_facility.h"

namespace Json {
	typedef enum {
		TOKEN_NONE,
		TOKEN_BEGIN,
		TOKEN_END,
		TOKEN_OBJECT_OPEN,
		TOKEN_OBJECT_CLOSE,
		TOKEN_ARRAY_OPEN,
		TOKEN_ARRAY_CLOSE,
		TOKEN_COLUMN,
		TOKEN_COMMA,
		TOKEN_TRUE,
		TOKEN_FALSE,
		TOKEN_NULL,
		TOKEN_NUMBER,
		TOKEN_STRING,
		TOKEN_ERROR	
	} TokenValue;

	class Token {
		public:
			Token(TokenValue ttype=TOKEN_NONE, std::string tval="") : _type(ttype), _value(tval) {}
			TokenValue type() const { return _type; }
			const std::string& value() const { return _value; }

			static Token EndToken;
		private:
			TokenValue _type;
			std::string _value;
	};

	class Tokenizer {
		public:
			Tokenizer(BufferIn *in=NULL) : _in(in), _next_char(0), _done(false), _unshift(false) { push_back(TOKEN_BEGIN,"(BEGIN)"); }
			bool load();
			Token pop();
			const Token& get(int pos) const;
			bool get_char(char &c) {
				if (_next_char==0) shift();
				if (_done) return false;
				c = _next_char;
				return true; 
			}
			bool shift() { 
				if (_unshift) 
					_unshift = false;
				else
					_done = !_in->read_char(_next_char); 
				return !_done; }

			void unshift(char c) { _next_char = c; _unshift = true; }

			std::string describe() const;	
		private:
			Tokenizer(const Tokenizer&);
			bool push_back(TokenValue t, const std::string& s="") { 
				_stack.push_back(Token(t,s)); return t!=TOKEN_ERROR; }
			std::vector<Token> _stack;
			BufferIn *_in;
			char _next_char;
			bool _done;
			bool _unshift;
	};

	class String: public Value {
		public:
			String(const std::string& str) : Value("string"), _string(str) {}

			virtual bool serialize(BufferOut *out, const std::string &prefix="") const;

			virtual Value* clone() const { 
				return(new String(_string)); }

			virtual bool is_string() { 
				return true; }
			
			virtual std::string as_string() const { 
				return _string; }
			
			virtual bool equals(const char *str) const { 
				return 0==strncmp(str,_string.data(),_string.size()); }
			
			virtual bool equals(const Value* val) const {
				return val->equals(_string); }

			virtual bool equals(const std::string& str)const { 
				return str==_string; }

			virtual double as_double() const { 
				return atof(_string.c_str()); }

			virtual int as_int() const { 
				return atoi(_string.c_str()); }

			virtual bool as_boolean() const	{ 
				return _string.size()>0; }

			virtual unsigned size() const { 
				return _string.size(); }

			virtual void clear() { 
				_string.clear(); }

		private:
			std::string _string;

			DISALLOW_COPY_AND_ASSIGN(String);
	};

	class Number: public Value {
		public:
			Number(double d) : Value("number"), _number(d) {}

			virtual Value* clone() const { 
				return(new Number(_number)); }

			virtual bool is_number() { 
				return true; }

			virtual std::string as_string() const { 
				char buf[20]; 
				snprintf(buf,20,"%g",_number); 
				return buf; }

			virtual double as_double() const { 
				return _number; }

			virtual int as_int() const { 
				return (int)_number; }

			virtual bool as_boolean() const	{ 
				return _number!=0; }

			virtual bool equals(const Value* val) const {
				return val->equals(_number); }

			virtual bool equals(int num) const { 
				return _number==(double)num; }

			virtual bool equals(double num) const { 
				return _number==num; }

			virtual unsigned size() const { 
				return 1; }
			
			virtual void clear() { 
				_number=0; }
		private:
			double _number;

			DISALLOW_COPY_AND_ASSIGN(Number);
	};

	class Boolean: public Value {
		public:
			Boolean(bool b) : Value("boolean"), _boolean(b) {}

			virtual Value* clone() const { 
				return(new Boolean(_boolean)); }

			virtual bool is_boolean() { 
				return true; }

			virtual std::string as_string() const { 
				if (_boolean) return "true"; 
				return "false"; }

			virtual double as_double() const { 
				if (_boolean) return 1; 
				return 0; }

			virtual int as_int() const { 
				if (_boolean) return 1; 
				return 0; }

			virtual bool as_boolean() const	{ 
				return _boolean; }

			virtual bool equals(const Value* val) const {
				return val->equals(_boolean); }

			virtual bool equals(bool b) const { 
				return b==_boolean; }

			virtual unsigned size() const { 
				return 1; }

			virtual void clear() { 
				_boolean = false; }
		private:
			bool _boolean;

			DISALLOW_COPY_AND_ASSIGN(Boolean);
	};

	class Null: public Value {
		public:
			Null() : Value("null") {}
			
			virtual Value* clone() const { 
				return(new Null()); }

			virtual bool is_null() { 
				return true; }

			virtual std::string as_string() const { 
				return "null"; }

			virtual bool as_boolean() const	{ 
				return false; }

			virtual bool equals(const Value* val) const {
				return val->is_null(); }

		private:
			DISALLOW_COPY_AND_ASSIGN(Null);
	};

	class Object : public Value {
		public:
			typedef Dictionary<Value*>::Iterator Iterator;

			Object() : Value("object") {}

			virtual bool serialize(BufferOut *out, const std::string &prefix="") const;

			virtual Value* clone() const;

			virtual bool is_object() const { 
				return true; }

			virtual int as_int() const { 
				return _object.size(); }

			virtual unsigned size() const { 
				return _object.size(); }
		
			virtual void clear();

			virtual Value* set(const std::string& index, Value *val) { 
				Value* nref;
				//logger.message(logger.LOG_DEBUG,"%p->set(%s,%p:%s)",this,index.c_str(),val,val->serialize().c_str());
				if (_object.get(index,nref)) 
					unreference(nref);
				_object.set(index,val);
				return this; } 

			virtual Value* get(const std::string& index) const { 
				Value* nref;
				if (_object.get(index,nref)) 
					return nref;
				return &Undefined; }

			virtual bool equals(const Value* val) const;

			virtual ~Object() { clear(); }

			//virtual bool merge_copy(Value *bt);
			//Iterator begin() const { return _object.begin(); }
			//Iterator end() const { return _object.end(); }
		private:
			Dictionary<Value*> _object;

			DISALLOW_COPY_AND_ASSIGN(Object);
	};

	class Array : public Value {
		public:
			Array() : Value("array") {}

			virtual bool serialize(BufferOut *out, const std::string &prefix="") const;
			//virtual bool serialize(std::string& result, const std::string &prefix="") const { 
			//	return serialize(result,prefix); }

			virtual Value* clone() const { 
				Array* o = new Array;
				for (unsigned i=0;i<_array.size();i++) o->_array.push_back(_array[i]->clone());
				return(o); 
			}

			virtual bool is_array() const 		{ return true; }

			virtual int as_int() const		{ return _array.size(); }

			virtual unsigned size() const 		{ return _array.size(); }
			
			virtual void clear();

			virtual ~Array() { clear(); }

			virtual Value* set(unsigned index, Value *val) {
				if (index<(unsigned)_array.size()) {
					unreference(_array[index]);
					_array[index] = val;
					return val;
				}
				return this; 
			}

			virtual Value* get(unsigned index) const { 
				if (index<(unsigned)_array.size()) 
					return _array[index];
				return &Undefined;
			}

			virtual Value* push_back(Value *val) {
				//logger.message(logger.LOG_DEBUG,"%p->push_back(%p:%s)",this,val,val->serialize().c_str());
			        _array.push_back(val);	
				return this; }

			virtual bool equals(const Value* val) const;

			//virtual bool merge_copy(Value *bt);

		private:
				std::vector<Value*> _array;

				DISALLOW_COPY_AND_ASSIGN(Array);
	};

	Value* unserialize(Tokenizer &tokenizer);
	Value* read_in_ptr(Tokenizer &tokenizer);

	bool escape_string(const std::string &src, std::string& dst);
	bool unescape_string(const std::string &src, std::string& dst);
}

Json::Value Json::Undefined;

std::string Json::_dummy;

Json::Token Json::Token::EndToken;

/*********************************
 * Json
 */
/*
void Json::unreference(Value* val)
{
	if (val->_ref_count)
	{
		logger.message(logger.LOG_DEBUG,"%p[%u]->dec_ref(%s)",val,val->_ref_count,val->serialize().c_str());	
		val->_ref_count--;
	}
	else
	{
		logger.message(logger.LOG_DEBUG,"%p->delete(%s)",val,val->serialize().c_str());	
		delete val;
	}
}*/

Json::Value* Json::create_object()
{
	return new Json::Object();
}

Json::Value* Json::create_array()
{
	return new Json::Array();
}

Json::Value* Json::create_string(const char *str)
{
	return new Json::String(str);
}

Json::Value* Json::create_string(const std::string& str)
{
	return new Json::String(str);
}

Json::Value* Json::create_number(double num)
{
	return new Json::Number(num);
}

Json::Value* Json::create_number(int num)
{
	return new Json::Number(num);
}

Json::Value* Json::create_boolean(bool b)
{
	return new Json::Boolean(b);
}

Json::Value* Json::create_null()
{
	return new Json::Null();
}

Json::Value* Json::unserialize(Tokenizer &tokenizer)
{
	Value *retval = Json::read_in_ptr(tokenizer);
	if (retval) 
		return retval;
	return &Undefined;
}

Json::Value* Json::read_in_ptr(Tokenizer &tokenizer)
{ 
	Token token;
	Value *retval = NULL;
	Value *extra;

	token = tokenizer.get(-1);

	switch (token.type()) {
		case Json::TOKEN_BEGIN:
			tokenizer.pop(); // Remove begin.
			tokenizer.load();
			return Json::read_in_ptr(tokenizer);

		case Json::TOKEN_END: 
			tokenizer.pop(); // Remove end
			//tokenizer.pop(); // BEGIN
			return NULL;

		case Json::TOKEN_OBJECT_OPEN: 
			retval = create_object();
			tokenizer.load();
			if (tokenizer.get(-1).type()==TOKEN_OBJECT_CLOSE)
			{
				tokenizer.pop(); // O_END
				tokenizer.pop(); // O_BEG
				return retval;
			}
			for (;;)
			{
				if (tokenizer.get(-1).type()!=TOKEN_STRING) goto json_read_in_fail;
				tokenizer.load();
				if (tokenizer.get(-1).type()!=TOKEN_COLUMN) goto json_read_in_fail;
				tokenizer.load();
				if ((extra = read_in_ptr(tokenizer))==NULL) goto json_read_in_fail;
				retval->set(tokenizer.get(-2).value(),extra);
				tokenizer.pop(); // COL
				tokenizer.pop(); // STR

				tokenizer.load();
				if (tokenizer.get(-1).type()==TOKEN_COMMA) 
				{ 
					tokenizer.pop();
					tokenizer.load();
				} 
				else if (tokenizer.get(-1).type()==TOKEN_OBJECT_CLOSE) 
				{
					tokenizer.pop(); // O_END
					tokenizer.pop(); // O_BEG
					return retval;
				} 
				else 
				{
					goto json_read_in_fail;
				}
			} 

		case Json::TOKEN_ARRAY_OPEN: 
			retval = create_array();
			tokenizer.load();
			if (tokenizer.get(-1).type()==TOKEN_ARRAY_CLOSE)
			{
				tokenizer.pop(); // A_END
				tokenizer.pop(); // A_BEG
				return retval;
			}
			for (;;)
			{
				if ((extra = read_in_ptr(tokenizer))==NULL) goto json_read_in_fail;
				retval->push_back(extra);

				tokenizer.load();
				if (tokenizer.get(-1).type()==TOKEN_COMMA) 
				{ 
					tokenizer.pop();
					tokenizer.load();
				} 
				else if (tokenizer.get(-1).type()==TOKEN_ARRAY_CLOSE) 
				{
					tokenizer.pop(); // A_END
					tokenizer.pop(); // A_BEG
					return retval;
				} 
				else 
				{
					goto json_read_in_fail;
				}
			} 
		case Json::TOKEN_STRING:
			retval = create_string(token.value());
			tokenizer.pop();
			return retval;

		case Json::TOKEN_NUMBER:
			retval = create_number(atof(token.value().c_str()));
			tokenizer.pop();
			return retval;

		case Json::TOKEN_TRUE:
			retval = create_boolean(true);
			tokenizer.pop();
			return retval;

		case Json::TOKEN_FALSE:
			retval = create_boolean(false);
			tokenizer.pop();
			return retval;

		case Json::TOKEN_NULL:
			retval = create_null();
			tokenizer.pop();
			return retval;

		default:
			break;
	}
json_read_in_fail:
	if (retval) Json::unreference(retval);
	return NULL;
}


Json::Value* Json::unserialize(BufferIn *in)
{
	Tokenizer tokenizer(in); 
	return unserialize(tokenizer);
}

Json::Value* Json::unserialize(const std::string &source)
{
	BufferInMemory *buffer=
		BufferInMemory::create(source.size(),(const unsigned char *)source.data());
	return unserialize(buffer); 
}


bool Json::escape_string(const std::string &src, std::string &result)
{
	char buf[10];
	std::string dst;

	dst.clear();

	for (unsigned i=0;i<src.size();i++)
	{
			switch (src[i]) {
				case 0x22: dst+="\\\""; break;
				case 0x5C: dst+="\\\\"; break;
				case 0x2F: dst+="\\/"; break;
				case 0x08: dst+="\\b"; break;
				case 0x0C: dst+="\\f"; break;
				case 0x0A: dst+="\\n"; break;
				case 0x0D: dst+="\\r"; break;
				case 0x09: dst+="\\t"; break;
				default: if (src[i]<32 && src[i]>=0)
					 {
						 sprintf(buf,"\\u%04X",(unsigned char)src[i]);
						 dst+=buf;
					 }
					 else
					 {
						 dst.push_back(src[i]);
					 }
			}
	}
	result = dst;
	return true;
}

bool Json::unescape_string(const std::string &src, std::string& result)
{
	char buf[5];
	unsigned num;
	unsigned i;
	std::string dst;

	dst.clear();

	for (i=0;i<src.size();i++)
	{
		if (src[i]=='\\')
		{
			if (++i==src.size()) return i-1;
			switch (src[i])	{
				case '"': dst+='\"'; break;
				case '\\': dst+='\\'; break;
				case '/': dst+='/'; break;
				case 'b': dst+='\b'; break;
				case 'f': dst+='\f'; break;
				case 'n': dst+='\n'; break;
				case 'r': dst+='\r'; break;
				case 't': dst+='\t'; break;			
				case 'u': if (i+5>=src.size()) return false;
					  i++;
					  buf[0]=src[i++];
					  buf[1]=src[i++];
					  buf[2]=src[i++];
					  buf[3]=src[i];
					  buf[4]=0;
					  num = strtol(buf,NULL,16);
					  if (num<256) // FIXME: incorrect approximation !
						  dst.push_back((char)num);
					  else	
						  dst.push_back((char)(num>>8));
					  	  dst.push_back((char)(num&0xFF));
					  break;
				default: return false; // illegal JSON escape
			}
		}
		else if (src[i]=='"') // TODO: do additional checks
		{
			return false;
		}
		else
		{
			dst+=src[i];
		}
	}
	result = dst;
	return true;
}

/********************************
 * Value
 */

unsigned total_count = 0; // for debug. purposes.

void* Json::Value::operator new(size_t sz)
{
	total_count++;
	return new char[sz];
}

void Json::Value::operator delete(void *target)
{
	total_count--;
	//logger.message(logger.LOG_DEBUG,"total count: %u",total_count);	
	delete [] (char*)target;
}

bool Json::Value::serialize(BufferOut *out, const std::string &prefix) const 
{
	return out->write_line(as_string()); 
}

std::string& Json::Value::serialize(std::string &result, const std::string &prefix) const
{
	BufferOutMemory *buffer=BufferOutMemory::create();
	if (serialize(buffer,prefix))
		result.assign((const char *)buffer->get_data(),buffer->get_size());
	else
		result.clear();
	delete buffer;
	return result; 
}

/********************************
 * Object
 */

bool Json::Object::serialize(BufferOut *out, const std::string &prefix) const 
{
	std::string key_str;
	Dictionary<Value*>::Iterator it;

	out->write_line("{ \n");
	
	it = _object.begin();
	while (it != _object.end())
	{
		//Json::escape_string(it.get().key(),key_str);
		key_str = it.get().key();
		out->write_line(prefix + "    \"" + key_str + "\" : ");
	       	if (!it.get().val()->serialize(out,prefix+"    "))
			return false;
		++it;
		if (it == _object.end())
			out->write_line("\n");
		else
			out->write_line(",\n");
	}
	return out->write_line(prefix + "}"); 
}

Json::Value* Json::Object::clone() const 
{
	Json::Object* o = new Object();
	Dictionary<Json::Value*>::Iterator it;
	for (it = _object.begin(); it != _object.end(); ++it)
		o->_object.set(it.get().key(),it.get().val()->clone());
	return o; 
}

void Json::Object::clear()
{
	Dictionary<Json::Value*>::Iterator it;
	for (it = _object.begin(); it != _object.end(); ++it) 
		Json::unreference(it.get().val()); 
	_object.clear();
}

bool Json::Object::equals(const Value* val) const
{
	Json::Value* nval;
	Dictionary<Json::Value*>::Iterator it;
	
	for (it = _object.begin(); it != _object.end(); ++it)
	{
		nval = val->get(it.get().key());
		if (!nval) return false;
		if (!nval->equals(it.get().val())) return false;
	}
	return true;
}


/********************************
 * Array
 */

bool Json::Array::serialize(BufferOut *out, const std::string &prefix) const 
{
	unsigned it;
	bool flat = true;

	for (it=0;it<_array.size();it++) 
		if (_array[it]->is_array() || _array[it]->is_object()) { flat = false; break; }

	out->write_line("[");
	for (it=0;it<_array.size();it++)
	{
		if (!flat) 
			out->write_line("\n" + prefix + "    ");
		else
			out->write_line(" ");
	       	if (!_array[it]->serialize(out,prefix+"    "))
			return false;
		if (it != _array.size()-1)
			out->write_line(",");
	}
	if (flat) return out->write_line(" ]");
	return out->write_line("\n"+prefix+"]");
}

bool Json::Array::equals(const Value* val) const
{
	for (unsigned it=0;it<_array.size();it++) 
		if (!_array[it]->equals(val->get(it))) return false;
	return true;
}

void Json::Array::clear() 
{ 
	for (unsigned it = 0; it<_array.size();it++) 
		Json::unreference(_array[it]); 
	_array.clear(); 
}


/********************************
 * String
 */

bool Json::String::serialize(BufferOut *out, const std::string &prefix) const 
{
	std::string r;
	
	Json::escape_string(_string,r);
	return out->write_line("\"" + r + "\""); 
}

/********************************
 * Tokenizer
 */

bool Json::Tokenizer::load()
{
	Json::Token token;
	std::string tstring;
	char c;
	int i;

	shift();
	for (;;) 
	{
		if (!get_char(c)) return push_back(Json::TOKEN_END,"(END)");
		if (c==' ' || c=='\t' || c=='\r' || c=='\n')
			shift();
		else
			break;
	}

	switch (c) {
		case 0: return push_back(Json::TOKEN_END,"(END)"); break;
		case '{': return push_back(Json::TOKEN_OBJECT_OPEN,"{"); break;
		case '}': return push_back(Json::TOKEN_OBJECT_CLOSE,"}"); break;
		case '[': return push_back(Json::TOKEN_ARRAY_OPEN,"["); break;
		case ']': return push_back(Json::TOKEN_ARRAY_CLOSE,"]"); break;
		case ',': return push_back(Json::TOKEN_COMMA,","); break;
		case ':': return push_back(Json::TOKEN_COLUMN,":"); break;
		case 't': tstring="t"; 
			  for (i=0;i<3;i++) { shift(); get_char(c); tstring.push_back(c); }
			  if (tstring=="true") return push_back(Json::TOKEN_TRUE,"true");
			  return push_back(Json::TOKEN_ERROR);
		case 'f': tstring="f"; 
			  for (i=0;i<4;i++) { shift(); get_char(c); tstring.push_back(c); }
			  if (tstring=="false") return push_back(Json::TOKEN_FALSE,"false");
			  return push_back(Json::TOKEN_ERROR);
		case 'n': tstring="n"; 
			  for (i=0;i<3;i++) { shift(); get_char(c); tstring.push_back(c); }
			  if (tstring=="null") return push_back(Json::TOKEN_NULL,"null");
			  return push_back(Json::TOKEN_ERROR);
		case '"': for(;;) {
				  shift();
				  if (!get_char(c)) return push_back(Json::TOKEN_ERROR);
			  	  if (c=='"') 
				  {
					  if (Json::unescape_string(tstring,tstring))
						  return push_back(Json::TOKEN_STRING,tstring);
					  return push_back(Json::TOKEN_ERROR);
				  }
				  tstring.push_back(c);
				  if (c=='\\') 
				  {
					  shift(); 
					  if (!get_char(c)) return push_back(Json::TOKEN_ERROR);	  
					  tstring.push_back(c);
				  }
			  }
			  break;
		default:  if ((c>='0' && c<='9') || c=='-')
			  {
				tstring.push_back(c);
				for (;;)
				{
					shift();
					if (!get_char(c)) return push_back(Json::TOKEN_ERROR);
				        if ((c>='0' && c<='9') || c=='.' || c=='e' || c=='E' || c=='-') 
						// TODO: fix this to more accurately match float nums.
					{
						tstring.push_back(c);
					}
					else
					{
						unshift(c);
						return push_back(Json::TOKEN_NUMBER,tstring);
					}
				}
			  }
	}
	return push_back(Json::TOKEN_ERROR);
}

Json::Token Json::Tokenizer::pop()
{
	Json::Token r;
	if (_stack.size())
	{
		r=_stack.back();
		_stack.pop_back();
		return r;
	}
	return Token(Json::TOKEN_END);
}

const Json::Token& Json::Tokenizer::get(int pos) const
{
	if (pos<0) pos+=(int)_stack.size();
	if (pos<0 || pos>=(int)_stack.size()) return Json::Token::EndToken; 
	return _stack[pos];
}

std::string Json::Tokenizer::describe() const 
{
	char buf[10];
	std::string retval;
	for (unsigned i=0;i<_stack.size();i++)
	{
		sprintf(buf,"@%i",_stack[i].type());
		if (_stack[i].value().size())
			retval+="/" + _stack[i].value();
		else
			retval+="/" + std::string(buf);
	}
	return retval;
}

#ifdef _TEST_JSON_
#include <iostream>

int main()
{
	//BufferIn *in = BufferInOutFile::create(0);
	Json::Value* val = Json::create_object();

	for (unsigned u=0;u<2;u++)
	{
		val->set("message","top");
		val->set("top",123);
		val->array("color")->push_back(1);
		val->array("color")->push_back(2);
		val->array("color")->push_back(3);


		std::cout << "Added " << total_count << std::endl;
		//std::string text;

		//val->serialize(text);

		std::cout << val->serialize() << std::endl;

		std::cout << "Exists: " << val->get("items")->serialize() << std::endl;
		//std::cout << "Count: " << val["items"].size() << std::endl;

		std::cout << "Done " << total_count << std::endl;
	}

	delete val;

	std::cout << "End " << total_count << std::endl;
	return 0;
}
#endif
